package com.xuexiang.xui_lib.component.button;

import ohos.agp.animation.AnimatorProperty;
import ohos.agp.components.Attr;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.StackLayout;
import ohos.agp.render.BlendMode;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.multimodalinput.event.TouchEvent;

import java.util.Optional;

/**
 * 水波纹点击效果
 *
 * @since 2021-04-16
 */
public class RippleView extends StackLayout implements Component.EstimateSizeListener, Component.TouchEventListener, Component.DrawTask {

    private static final String SIMPLE = "simple"; // 简单波纹
    private static final String RECTANGLE = "rect"; // 方形波纹
    private static final String DOUBLE = "double"; // 方形波纹

    private int WIDTH;
    private int HEIGHT;
    private float mDownX = -1;
    private float mDownY = -1;
    private int mRippleDuration = 200;
    private int mFrameRate = 20;
    private float mRippleAlpha = 0.5F;
    private int mRipplePadding;
    private int mZoomDuration;
    private float mZoomScale;
    private boolean mHasToZoom;
    private boolean isCentered;
    private float mRadiusMax = 0;
    private boolean mAnimationRunning = false;
    private int mTimer = 0;
    private int mDurationEmpty = -1;
    private int mTimerEmpty = 0;
    private int mRippleType;


    private Paint mPaint;
    private Paint mEmptyPaint;
    private Color rippleColor;
    private EventHandler canvasHandler;
    private AnimatorProperty mScaleAnimation;
    private OnRippleCompleteListener onCompletionListener;

    private final Runnable runnable = this::invalidate;

   /* public RippleView(Context context) {
        this(context, null);
    }*/

    public RippleView(Context context, AttrSet attrSet) {
        this(context, attrSet, null);
    }

    public RippleView(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        init(context, attrSet);
    }

    private void init(final Context context, final AttrSet attrs) {
        canvasHandler = new EventHandler(EventRunner.getMainEventRunner());
        setEstimateSizeListener(this);
        setTouchEventListener(this);
        initAttrs(context, attrs);
        initPaint();
        addDrawTask(this);
        this.setClickable(true);
    }

    private void initAttrs(Context context, AttrSet attrSet) {
        Optional<Attr> ripple_type = attrSet.getAttr("rippleType");

        if (ripple_type.isPresent()) {
            String rippleName = ripple_type.get().getStringValue();
            if (SIMPLE.equals(rippleName)) {
                mRippleType = RippleType.SIMPLE.type;
            } else if (RECTANGLE.equals(rippleName)) {
                mRippleType = RippleType.RECTANGLE.type;
            } else if (DOUBLE.equals(rippleName)) {
                mRippleType = RippleType.DOUBLE.type;
            } else {
                mRippleType = RippleType.SIMPLE.type;
            }
        } else {
            mRippleType = RippleType.SIMPLE.type;
        }

        Optional<Attr> ripple_color = attrSet.getAttr("rippleColor");
        if (ripple_color.isPresent()) {
            Color colorValue = ripple_color.get().getColorValue();
            if (colorValue != null && colorValue.getValue() != 0) {
                rippleColor = colorValue;
            }
        } else {
            rippleColor = new Color(Color.getIntColor("#FFFFFF"));
        }
        Optional<Attr> ripple_padding = attrSet.getAttr("ripplePadding");
        mRipplePadding = ripple_padding.map(Attr::getIntegerValue).orElse(0);
        Optional<Attr> rv_zoom = attrSet.getAttr("rvZoom");

        mHasToZoom = rv_zoom.map(Attr::getBoolValue).orElse(false);

        Optional<Attr> rv_centered = attrSet.getAttr("rvCentered");
        isCentered = rv_centered.map(Attr::getBoolValue).orElse(false);

        Optional<Attr> zoom_scale = attrSet.getAttr("zoomScale");
        mZoomScale = zoom_scale.map(Attr::getFloatValue).orElse(0.0F);

        Optional<Attr> zoom_duration = attrSet.getAttr("zoomDuration");
        mZoomDuration = zoom_duration.map(Attr::getIntegerValue).orElse(200);
    }

    private void initPaint() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL_STYLE);
        mPaint.setColor(rippleColor);
        mPaint.setAlpha(mRippleAlpha);
        //this.setWillNotDraw(false);

        mEmptyPaint = new Paint();
        mEmptyPaint.setBlendMode(BlendMode.SRC_IN);
        mEmptyPaint.setColor(new Color(Color.getIntColor("#00000000")));
    }

    @Override
    public boolean onEstimateSize(int widthSpec, int heightSpec) {
        WIDTH = EstimateSpec.getSize(widthSpec);
        HEIGHT = EstimateSpec.getSize(heightSpec);
        mScaleAnimation = new AnimatorProperty();
        mScaleAnimation.setDuration(mZoomDuration);
        mScaleAnimation.scaleX(mZoomScale);
        mScaleAnimation.scaleY(mZoomScale);
        mScaleAnimation.setLoopedCount(1);
        mScaleAnimation.setTarget(this);
        return false;
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        switch (touchEvent.getAction()) {
            case TouchEvent.PRIMARY_POINT_DOWN:
                animateRipple(touchEvent);
                break;
        }
        return false;
    }

    private void animateRipple(TouchEvent event) {
        createAnimation(event.getPointerPosition(0).getX(), event.getPointerPosition(0).getY());
    }

    private void createAnimation(float x, float y) {
        if (this.isEnabled() && !mAnimationRunning) {
            if (mHasToZoom) {
                mScaleAnimation.start();
            }
            mRadiusMax = Math.max(WIDTH, HEIGHT);
            if (mRippleType != 2) {
                mRadiusMax /= 2;
            }
            mRadiusMax -= mRipplePadding;
            if (isCentered || mRippleType == 1) {
                this.mDownX = getEstimatedWidth() >> 1;
                this.mDownY = getEstimatedHeight() >> 1;
            } else {
                this.mDownX = x;
                this.mDownY = y;
            }
            mAnimationRunning = true;
            invalidate();
        }
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        if (mAnimationRunning) {
            canvas.save();
            if (mRippleDuration < mTimer * mFrameRate) {
                mAnimationRunning = false;
                mDurationEmpty = -1;
                mTimerEmpty = 0;
                mTimer = 0;
                canvas.restore();
                invalidate();
                if (onCompletionListener != null) {
                    onCompletionListener.onComplete(this);
                }
                return;
            } else {
                canvasHandler.postTask(runnable, mFrameRate);
            }

            if (mTimer == 0) {
               // canvas.save();
            }
            canvas.drawCircle(mDownX, mDownY, (mRadiusMax * (((float) mTimer * mFrameRate) / mRippleDuration)), mPaint);
            float alpha = mRippleAlpha - ((mRippleAlpha) * (((float) mTimer * mFrameRate) / mRippleDuration));
            mPaint.setAlpha(alpha);

            if (mRippleType == 1 && (((float) mTimer * mFrameRate) / mRippleDuration) > 0.4f) {
                if (mDurationEmpty == -1) {
                    mDurationEmpty = mRippleDuration - mTimer * mFrameRate;
                }
                mTimerEmpty++;
                float radius = ((mRadiusMax) * (((float) mTimerEmpty * mFrameRate) / (mDurationEmpty)));
                canvas.drawCircle(mDownX, mDownY, radius, mEmptyPaint);
                mEmptyPaint.setAlpha((mRippleAlpha - ((mRippleAlpha) * (((float) mTimerEmpty * mFrameRate) / (mDurationEmpty)))));
            }

            mTimer++;
        }
    }

    /**
     * Launch Ripple animation for the current view centered at x and y position
     *
     * @param x Horizontal position of the ripple center
     * @param y Vertical position of the ripple center
     */
    public void animateRipple(final float x, final float y) {
        createAnimation(x, y);
    }

    /**
     * Set Ripple color, default is #FFFFFF
     *
     * @param rippleColor New color resource
     */
    public void setRippleColor(Color rippleColor) {
        this.rippleColor = rippleColor;
    }

    public Color getRippleColor() {
        return rippleColor;
    }

    public RippleType getRippleType() {
        return RippleType.values()[mRippleType];
    }

    /**
     * Set Ripple type, default is RippleType.SIMPLE
     *
     * @param mRippleType New Ripple type for next animation
     */
    public void setRippleType(final RippleType mRippleType) {
        this.mRippleType = mRippleType.ordinal();
    }

    public Boolean isCentered() {
        return isCentered;
    }

    /**
     * Set if ripple animation has to be centered in its parent view or not, default is False
     *
     * @param isCentered
     */
    public void setCentered(final Boolean isCentered) {
        this.isCentered = isCentered;
    }

    public int getRipplePadding() {
        return mRipplePadding;
    }

    /**
     * Set Ripple padding if you want to avoid some graphic glitch
     *
     * @param ripplePadding New Ripple padding in pixel, default is 0px
     */
    public void setRipplePadding(int ripplePadding) {
        this.mRipplePadding = ripplePadding;
    }

    public Boolean isZooming() {
        return mHasToZoom;
    }

    /**
     * At the end of Ripple effect, the child views has to zoom
     *
     * @param hasToZoom Do the child views have to zoom ? default is False
     */
    public void setZooming(Boolean hasToZoom) {
        this.mHasToZoom = hasToZoom;
    }

    public float getZoomScale() {
        return mZoomScale;
    }

    /**
     * Scale of the end animation
     *
     * @param zoomScale Value of scale animation, default is 1.03f
     */
    public void setmZoomScale(float zoomScale) {
        this.mZoomScale = zoomScale;
    }

    public int getZoomDuration() {
        return mZoomDuration;
    }

    /**
     * Duration of the ending animation in ms
     *
     * @param zoomDuration Duration, default is 200ms
     */
    public void setmZoomDuration(int zoomDuration) {
        this.mZoomDuration = zoomDuration;
    }

    public int getRippleDuration() {
        return mRippleDuration;
    }

    /**
     * Duration of the Ripple animation in ms
     *
     * @param mRippleDuration Duration, default is 400ms
     */
    public void setRippleDuration(int mRippleDuration) {
        this.mRippleDuration = mRippleDuration;
    }

    public int getmFrameRate() {
        return mFrameRate;
    }

    /**
     * Set framerate for Ripple animation
     *
     * @param frameRate New framerate value, default is 10
     */
    public void setFrameRate(int frameRate) {
        this.mFrameRate = frameRate;
    }

    public float getRippleAlpha() {
        return mRippleAlpha;
    }

    /**
     * Set alpha for ripple effect color
     *
     * @param mRippleAlpha Alpha value between 0 and 255, default is 90
     */
    public void setRippleAlpha(int mRippleAlpha) {
        this.mRippleAlpha = mRippleAlpha;
    }

    public void setOnRippleCompleteListener(OnRippleCompleteListener listener) {
        this.onCompletionListener = listener;
    }

    /**
     * Defines a callback called at the end of the Ripple effect
     */
    public interface OnRippleCompleteListener {
        /**
         * 波纹结束
         *
         * @param rippleView
         */
        void onComplete(RippleView rippleView);
    }

    /**
     * 波纹类型
     */
    public enum RippleType {
        /**
         * 单波纹
         */
        SIMPLE(0),
        /**
         * 双波纹
         */
        DOUBLE(1),
        /**
         * 方形波纹
         */
        RECTANGLE(2);

        int type;

        RippleType(int type) {
            this.type = type;
        }
    }

}
